
#include <BFont>
#include <BPainter>
#include <BMouseEvent>
#include <BKeyEvent>
#include <BScrollBar>

#include "IAlignment.h"
#include "IComboBoard.h"
#include "member_BObject.h"
#include "member_BStyle.h"
#include "member_BComboItem.h"
#include "member_BComboBox.h"

using namespace BWE;

IComboBoard::IComboBoard(BArray<BComboItemHolder>* items, BComboBox* comboBox) : BScrollArea(comboBox)
{
	this->comboBox = comboBox;
	this->items = items;
	this->setVisible(false);
	this->setScrollHint(ScrollHint_Floating);

	dirty = true;
	spacing = 3;
	hoveredItem = 0;
	pressedItem = 0;
	maxShowCount = 10;

}
IComboBoard::~IComboBoard()
{

}

void IComboBoard::freshItemRects()
{
	const BRect& crect = this->centerRect();
	const BFont* font = this->realFont();
	const BStyle* style = comboBox->realStyle();
	maxItemSize.reset();
	contentSize.reset();
	hintSize.reset();
	if (items->size())
	{
		int y = 0;
		for (int i = 0; i < items->size(); i++)
		{
			BComboItem* item = (*items)[i];
			item_member(item)->board = this;
			if (item_member(item)->dirtyStyle)
			{
				BIcon* icon = style->icon(item->name());
				if (!icon)
					icon = style->icon(item->text());
				if (icon)
					item->setIcon(icon);
				item_member(item)->dispenseStyles(style);
				item_member(item)->applyStyle(style);
				item_member(item)->dirtyStyle = false;
				item_member(item)->dirty = true;
			}
			if (item_member(item)->dirty)
			{
				item_member(item)->freshSize(font, 0);
				item_member(item)->dirty = false;
			}
			BSize realSize = item_member(item)->hintSize;

			if (maxShowCount < 1 || i < maxShowCount)
			{
				if (hintSize.width() < realSize.width())
					hintSize.width() = realSize.width();
				hintSize.height() += realSize.height() + spacing;
			}

			if (maxItemSize.width() < realSize.width())
				maxItemSize.width() = realSize.width();
			if (maxItemSize.height() < realSize.height())
				maxItemSize.height() = realSize.height();

			if (contentSize.width() < realSize.width())
				contentSize.width() = realSize.width();
			contentSize.height() += realSize.height() + spacing;

			realSize.width() = crect.width();

			item_member(item)->setPos(0, y);
			item_member(item)->setSize(realSize);
			y += realSize.height() + spacing;
		}
		hintSize.height() -= spacing;
		contentSize.height() -= spacing;
	}
	BRect area = this->area();
	area.setSize(contentSize);
	this->setArea(area);
}

BSize IComboBoard::sizeHint() const
{
	if (dirty)
	{
		((IComboBoard*)this)->freshItemRects();
		dirty = false;
	}
	return this->preferSize(hintSize);
}

void IComboBoard::scrolling(Orientation orientation)
{
	showBegin = -1;
	showEnd = -1;
	BRect crect = this->centerRect();
	BPoint offset = this->offset();
	for (int i = 0; i < items->size(); i++)
	{
		BComboItem* item = (*items)[i];
		BRect rect = item_member(item)->rect() + offset;
		if (rect.intersect(crect))
		{
			showBegin = i;
			showEnd = i;
			break;
		}
	}
	if (showBegin >= 0)
	{
		while (++showEnd < items->size())
		{
			BComboItem* item = (*items)[showEnd];
			BRect rect = item_member(item)->rect() + offset;
			if (rect.top() > crect.bottom())
				break;
		}
	}
	this->fresh();
	BScrollArea::scrolling(orientation);
}

void IComboBoard::resizeEvent(const BEvent& event)
{
	dirty = true;
	BScrollArea::resizeEvent(event);
}

void IComboBoard::styleEvent(const BEvent& event)
{
	BScrollArea::styleEvent(event);
	const BStyle* style = event.value();
	style_member(style)->assign(Value_Offset, popupOffset);
	dirty = true;
}
void IComboBoard::freshEvent(const BEvent& event)
{
	if (dirty)
	{
		freshItemRects();
		dirty = false;
	}
	BScrollArea::freshEvent(event);
}
void IComboBoard::paintEvent(const BEvent& event)
{
	BScrollArea::paintEvent(event);

	BComboItem* current = comboBox->current();
	const BRect& crect = this->centerRect();
	BPoint offset = this->offset();

	BPainter painter(this);
	State state = painter.state();

	painter.setClip(crect);
	for (int i = showBegin; i < showEnd; i++)
	{
		BComboItem* item = (*items)[i];
		State itemState = item->enabled() ? state : State_Disable;
		if (hoveredItem == item)
		{
			if (pressedItem == item)
				itemState |= State_Pressed;
			else
				itemState |= State_Hovered;
		}
		else if (current == item)
		{
			itemState |= State_Selected;
		}

		painter.pushMatrix();
		painter.translate(item_member(item)->pos + offset);
		//painter.setClip(item_member(item)->rect() & crect);
		painter.setStyle(item_member(item)->realStyle);
		item_member(item)->drawBackground(painter, itemState);
		if (item_member(item)->margin.valid())
			item_member(item)->drawMargin(painter, itemState);
		if (item_member(item)->border > 0)
			item_member(item)->drawBorder(painter, itemState);
		item_member(item)->drawContent(painter, itemState);
		painter.popMatrix();

	}
}

void IComboBoard::keyPressEvent(const BKeyEvent& keyEvent)
{
	if (keyEvent.key() == Key_Enter)
	{
		comboBox->setCurrent(hoveredItem);
		this->hide();
		keyEvent.accept();
	}

	int index = items->find(hoveredItem);
	if (keyEvent.key() == Key_Up)
	{
		index--;
		if (index < 0)
			index = 0;
		keyEvent.accept();
	}
	if (keyEvent.key() == Key_Down)
	{
		index++;
		if (index >= items->size())
			index = items->size() - 1;
		keyEvent.accept();
	}
	if (hoveredItem != items->at(index))
	{
		hoveredItem = items->at(index);
		fresh();
	}

	BScrollArea::keyPressEvent(keyEvent);
}

void IComboBoard::mouseEnterEvent(const BMouseEvent& mouseEvent)
{
	if (hoveredItem)
	{
		hoveredItem = 0;
		this->fresh();
	}
	BScrollArea::mouseEnterEvent(mouseEvent);
}
void IComboBoard::mouseLeaveEvent(const BMouseEvent& mouseEvent)
{
	if (hoveredItem)
	{
		hoveredItem = 0;
		this->fresh();
	}
	BScrollArea::mouseLeaveEvent(mouseEvent);
}

void IComboBoard::mouseClickEvent(const BMouseEvent& mouseEvent)
{
	if (hoveredItem && hoveredItem->enabled())
	{
		comboBox->setCurrent(hoveredItem);
		object_member(comboBox)->emit(Signal_ItemClicked, hoveredItem);
	}
	mouseEvent.accept();
	BScrollArea::mouseClickEvent(mouseEvent);
}
void IComboBoard::mouseMoveEvent(const BMouseEvent& mouseEvent)
{
	const BPoint& pos = mouseEvent.pos();
	BPoint offset = this->offset();
	BComboItem* itemHovered = 0;
	for (int i = showBegin; i < showEnd; i++)
	{
		BComboItem* item = (*items)[i];
		BRect rect = item_member(item)->rect() + offset;
		if (rect.contain(pos))
		{
			if (item->enabled())
				itemHovered = item;
			break;
		}
	}
	if (hoveredItem != itemHovered)
	{
		hoveredItem = itemHovered;
		object_member(comboBox)->emit(Signal_ItemHovered, hoveredItem);
		fresh();
	}
	BScrollArea::mouseMoveEvent(mouseEvent);
}
